import pygame, sys, random

"""定义背景类，实现背景滚动"""

class Background(pygame.sprite.Sprite):
    def __init__(self, picture, isout = False):
        """初始化背景类，picture是背景图片，Flase用于指定两张图片"""
        pygame.sprite.Sprite.__init__(self)
        self.image = picture
        self.rect = picture.get_rect()
        self.speed = 1                  # 背景移动速度
        if isout:
            self.rect.x = bgwidth       # 另一个背景图片在当前图片左侧
    def update(self):
        self.rect.x -= self.speed
        if self.rect.x <= -bgwidth:
            self.rect.x = bgwidth


"""屏幕绘制方法"""


def blit_screen(msg, size, pos):
    if size == '':
        screen.blit(msg, pos)
    else:
        screen.blit(pygame.transform.scale(msg, size), pos)


"""添加泡泡组方法"""


def add_bubble(group, num):
    """生成指定数量泡泡，group为泡泡组，num为生成泡泡数量"""
    for i in range(num):
        bb = Bubble()
        group.add(bb)


""" 定义鸟类继承自pygame精灵类 """

class Love(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.rect = love_image.get_rect(x=uppip.rect.x,y=uppip.rect.bottom)
        self.mask = pygame.mask.from_surface(love_image)
        self.yspeed = 1
        self.up = False
        self.flag = True
        self.random = random.randint(1, 8)


    def love_move(self, top, bottom):
        if self.up :
            self.rect.y -= self.yspeed
            if self.rect.y <= top:
                self.rect.y += self.yspeed
                self.up = not self.up
        else:
            self.rect.y += self.yspeed
            if self.rect.y >= bottom - self.rect.height:
                self.rect.y -= self.yspeed
                self.up = not self.up

    def eat_love(self):
        if self.random == 2:
            love.rect.x = uppip.rect.x + 11
            love.love_move(uppip.rect.bottom, downpip.rect.y)
            if  self.flag:
                blit_screen(love_image, '', (love.rect.x, love.rect.y))



class Bird(pygame.sprite.Sprite):

    def __init__(self):
        """小鸟类初始化方法"""
        pygame.sprite.Sprite.__init__(self)
        self.rect = bird1.get_rect(x=80, y=200)  # 小鸟初始位置
        self.jump = False  # 判断是否跳跃
        self.jumpspeed = 5  # 小鸟初始跳跃加速度
        self.gravity = 1  # 小鸟初始重力加速度

    def bird_jump(self):
        """定义小鸟跳跃方法"""
        if self.jump:
            if game.level == 2:
                self.jumpspeed -= 0.3
            else:
                self.jumpspeed -= 0.5  # 小鸟跳跃，跳跃加速度-0.5，y坐标-5
            self.rect.y -= self.jumpspeed
        else:
            if game.level == 2:
                self.gravity += 0.1
            else:
                self.gravity += 0.2  # 小鸟下落，重力加速度+0.2，y坐标+1
            self.rect.y += self.gravity
        self.rect[1] = self.rect.y  # 更新矩形的y坐标

    def bird_update(self, f, p1, p2, p3):
        """实现小鸟动态跳跃"""
        if f % 3 == 1:
            bird.mask = pygame.mask.from_surface(p1)
            blit_screen(p1, '', bird.rect)
        elif f % 3 == 2:
            bird.mask = pygame.mask.from_surface(p2)
            blit_screen(p2, '', bird.rect)
        else:
            bird.mask = pygame.mask.from_surface(p3)
            blit_screen(p3, '', bird.rect)
        pygame.display.update()


""" 定义管道类继承自pygame精灵类 """


class Pipeline(pygame.sprite.Sprite):

    def __init__(self, posy, picture):
        """定义管道初始化"""
        pygame.sprite.Sprite.__init__(self)
        self.mask = pygame.mask.from_surface(picture)  # 管道像素遮罩
        self.rect = picture.get_rect(x=400, y=posy)  # 管道初始位置
        self.xspeed = 3  # 管道移动速度
        self.yspeed = 0.5
        self.is_up = True
        self.flag = True

    def pipeline_move(self):
        """定义管道移动方法"""
        global score, lives
        uppip.rect.x -= self.xspeed  # 上下管道x坐标-3
        downpip.rect.x -= self.xspeed

        if score > 20:              # 分数超过20管道开始上下移动
            if self.is_up:
                uppip.rect.bottom -= self.yspeed
                downpip.rect.bottom -= self.yspeed
                if uppip.rect.bottom <= 0:
                    self.is_up = False
            if not self.is_up:
                uppip.rect.y += self.yspeed
                downpip.rect.y += 1             # ！！！！！这里有点问题！！！为什么只能是1
                if downpip.rect.top >= bgheight:
                    self.is_up = True

        if uppip.rect.x + uppip.rect.width == bird.rect.x:  # 判断小鸟是否越过管道，若越过，加一分
            score += 1

        if uppip.rect.x < -52:  # 管道移出画面，恢复管道初始x位置，随机设置y位置
            uppip.rect.x = bgwidth
            downpip.rect.x = bgwidth
            uppip.rect.y = random.randint(100-bgheight,350-bgheight)

            downpip.rect.y = uppip.rect.bottom + random.randint(70, 150)
            love.random = random.randint(1, 9)
            love.flag = True
            self.flag = True
            print(love.random)







""" 定义泡泡类继承自pygame精灵类 """


class Bubble(pygame.sprite.Sprite):

    def __init__(self):
        """定义泡泡初始化"""
        pygame.sprite.Sprite.__init__(self)
        # self.image = pygame.image.load("./picture/bubble.png")
        self.rect = bubble_image2.get_rect(x=bgwidth, y=random.randint(0, 300))  # 泡泡初始位置
        self.mask = pygame.mask.from_surface(bubble_image2)  # 泡泡像素遮罩
        self.xspeed = random.randint(2, 5)  # 泡泡初始x速度
        self.yspeed = random.randint(-2, 3)  # 泡泡初始y速度

    def bubble_move(self):
        """定义泡泡移动方法"""
        global score, cnt, bubble_num
        self.rect.x -= self.xspeed  # 泡泡xy坐标依据速度变化self.rect.x == -self.rect.width
        self.rect.y += self.yspeed
        if self.rect.x == -self.rect.width-20 or self.rect.bottom == 0 or self.rect.top == bgheight:  # 小鸟越过泡泡，每越过一组加一分
            cnt += 1
            if cnt % bubble_num == 0:
                score += 1
                if cnt % (3*bubble_num) == 0:
                    bubble_num += 1
                    cnt = 0
                    bubble_group.empty()
                    add_bubble(bubble_group,bubble_num)
                for each in bubble_group:
                    each.rect.x = bgwidth                           # 初始化泡泡位置
                    # each.rect.y = random.randint(bird.rect.y - 200, bird.rect.y + 200)
                    each.rect.y = random.randint(0,300)

""" 定义音乐类 """


class Music(object):

    def __init__(self):
        pygame.mixer.music.load("./music/flappybird.mp3")

    def background_music(self, flag):
        """控制背景音乐播放"""
        if flag:
            pygame.mixer.music.play(-1, 0.0)
        else:
            pygame.mixer.music.stop()

    def jump_music(self, flag):
        """控制小鸟跳跃音效播放"""
        if flag:
            jump = pygame.mixer.Sound("./music/jump.mp3")
            pygame.mixer.Sound.set_volume(jump, 0.3)
            jump.play()

    def dead_music(self, flag):
        """音效播放"""
        if flag:
            dead = pygame.mixer.Sound("./music/fail.mp3")
            dead.play()


""" 定义按钮类 """


class Button(object):

    def __init__(self):
        """初始化按钮位置和大小"""
        self.selfxy = self.selfx, self.selfy = 120, 60
        self.srposxy = self.srposx, self.srposy = 140, 300
        self.mqposxy = self.mqposx, self.mqposy = 140, 400


""" 定义flappybird游戏类 """


class Game(object):

    def __init__(self):
        """初始化游戏阶段等级"""
        self.level = 1
        self.groups = pipeline_group



    def check_dead(self, groups):
        """检查小鸟是否死亡方法（group用于传递当前障碍物组）"""
        global lives
        if pygame.sprite.spritecollide(bird, groups, False, pygame.sprite.collide_mask):
            if lives > 0 and uppip.flag:
                lives -= 1
                uppip.flag = False
                return False
            if lives ==0 and uppip.flag:
                return True
        elif bird.rect.y <= 0 or bird.rect.y + bird.rect.height >= bgheight:
            lives = 0
            return True
        else:
            return False



    def updata_screen(self, l):
        """
        实时更新屏幕方法（l用于传递当前游戏阶段等级）:
        分别绘制游戏背景/障碍物/分数
        调用障碍物/小鸟移动方法
        """
        global lives
        clock.tick(60)
        bg_group.update()  # 绘制游戏背景
        bg_group.draw(screen)
        if l == 1:
            uppip.pipeline_move()                                               # 调用管道移动方法
            love.eat_love()
            blit_screen(pineup, '', (uppip.rect.x, uppip.rect.y))               # 绘制上下管道
            blit_screen(pinedown, '', (downpip.rect.x, downpip.rect.y))
            if pygame.sprite.collide_mask(bird, love) and love.flag:
                lives += 1
                love.flag = False

        if l == 2:
            # bubble_group.draw(screen)
            for each in bubble_group:                                           # 遍历"组"，调用泡泡移动方法，绘制泡泡
                each.bubble_move()
                blit_screen(bubble_image2, '', each.rect)

        score_font.render("Score : " + str(score), True, (255, 255, 255))           # 设置分数字体内容、抗锯齿、颜色
        bird.bird_jump()
        blit_screen(score_font.render("Score : " + str(score), True, (255, 255, 255)), '', (10, 0))
        blit_screen(love_image, '', (10, 50))
        blit_screen(score_font.render(" x " + str(lives), True, (255, 255, 255)), '', (45, 30))


    def get_result(self, flag):
        """
        返回游戏结果方法（flag用于控制小鸟死亡音效）：
        关闭背景音乐
        绘制gameover/分数/晕倒的小鸟/返回按钮/退出按钮并显示在屏幕上
        """
        music.dead_music(flag)                                                          # 播放小鸟死亡音效
        music.background_music(False)                                                   # 关闭背景音乐
        gameover_text = 'Game Over'                                                     # 设置文本内容
        self.gameover = myfont.render(gameover_text, True, (242, 3, 36))                # 设置文本内容、抗锯齿、颜色
        self.go_selfxy = myfont.size(gameover_text)[0], myfont.size(gameover_text)[1]   # 获取文本大小
        blit_screen(star, '', (bird.rect.x, bird.rect.y))                               # 绘制晕倒的小鸟
        blit_screen(self.gameover, self.go_selfxy, (75, 120))                           # 绘制gameover
        blit_screen(return_image, sr_button.selfxy, sr_button.srposxy)                  # 绘制返回按钮
        blit_screen(quit_image, mq_button.selfxy, mq_button.mqposxy)                    # 绘制退出按钮
        pygame.display.update()                                                         # 屏幕显示

    def begin(self):
        """
        游戏开始方法：
        点击开始按钮进入游戏
        level1为城市
        level2为海洋
        点击音乐按钮控制音乐开关
        游戏结束后点击返回按钮回到主界面
        游戏结束后点击退出按钮退出游戏
        """
        switch = True                                                           # 音乐开关，默认为开
        music.background_music(switch)                                          # 播放音乐
        f = 1  # 小鸟图片序号1-3
        delay = 99
        while True:
            clock.tick(60)                                                      # 设置时间间隔60ms
            blit_screen(bg_land, bgsize, (0, 0))                                # 绘制游戏背景
            blit_screen(wt, title_selfxy, title_posxy)                          # 绘制游戏标题
            blit_screen(start_image, sr_button.selfxy, sr_button.srposxy)       # 绘制开始按钮
            blit_screen(music_image, mq_button.selfxy, mq_button.mqposxy)       # 绘制音乐开关按钮
            if not switch:
                blit_screen(quit_music, mq_button.selfxy, mq_button.mqposxy)
            bird.bird_update(f, bird1, bird2, bird3)
            delay -= 1
            if not (delay % 6):
                f += 1
            if not delay:  # 初始化延时
                f = 1
                delay = 99

            # ↓获取鼠标事件:quit=退出游戏/点击开始按钮=进入游戏/连续点击音乐按钮=开关音乐
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()                                                  # 退出游戏
                if event.type == pygame.MOUSEBUTTONDOWN and mq_button.mqposx < \
                        pygame.mouse.get_pos()[0] < mq_button.mqposx + \
                        mq_button.selfx and mq_button.mqposy < \
                        pygame.mouse.get_pos()[1] < mq_button.mqposy + \
                        mq_button.selfy:
                    switch = not switch                                         # 交替变化实现每次点击切换音乐状态
                    music.background_music(switch)
                if event.type == pygame.MOUSEBUTTONDOWN and sr_button.srposx < \
                        pygame.mouse.get_pos()[0] < sr_button.srposx + \
                        sr_button.selfx and sr_button.srposy < \
                        pygame.mouse.get_pos()[1] < sr_button.srposy + \
                        sr_button.selfy:
                    self.updata_screen(self.level)                              # 进入游戏,绘制游戏中的屏幕
                    # f = 1                                                       # 小鸟图片序号1-3
                    # delay = 99                                                  # 延时
                    while True:
                        clock.tick(60)
                        if self.level == 1:
                            bird.bird_update(f, bird1, bird2, bird3)
                        if self.level == 2:
                            bird.bird_update(f, bird4, bird5, bird6)
                        delay -= 1
                        if not (delay % 3):
                            f += 1
                        if not delay:                                           # 初始化延时
                            f = 1
                            delay = 99

                        # ↓获取事件:quit=退出游戏;鼠标点击/按键盘=小鸟跳跃
                        for event in pygame.event.get():
                            if event.type == pygame.QUIT:
                                sys.exit()
                            if (event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN):
                                music.jump_music(switch)                        # 播放跳跃声音
                                bird.jump = True                                # 小鸟跳跃
                                bird.gravity = 1                                # 初始化重力加速度
                                bird.jumpspeed = 5                              # 初始化跳跃阻力

                        # ↓若小鸟死亡，返回结果，加载游戏结束界面;若没有死亡，则加载游戏界面
                        if self.check_dead(self.groups):
                            self.get_result(switch)                             # 返回游戏结果

                            # ↓获取事件:quit按钮=退出游戏;return按钮=返回游戏准备界面;
                            while True:
                                for event in pygame.event.get():
                                    if event.type == pygame.QUIT:
                                        sys.exit()
                                    if event.type == pygame.MOUSEBUTTONDOWN and mq_button.mqposx < \
                                            pygame.mouse.get_pos()[0] < mq_button.mqposx + \
                                            mq_button.selfx and mq_button.mqposy < \
                                            pygame.mouse.get_pos()[1] < mq_button.mqposy + \
                                            mq_button.selfy:
                                        pygame.quit()                       # 卸载pygame所有模块
                                        sys.exit()
                                    if event.type == pygame.MOUSEBUTTONDOWN and sr_button.srposx < \
                                            pygame.mouse.get_pos()[0] < sr_button.srposx + \
                                            sr_button.selfx and sr_button.srposy < \
                                            pygame.mouse.get_pos()[1] < sr_button.srposy + \
                                            sr_button.selfy:
                                        return                              # 回到游戏主界面
                        else:
                            # ↓实时更新屏幕内容：背景/管道/小鸟/分数

                            self.updata_screen(self.level)
                            if score == 40:                                   # 根据分数设置游戏阶段
                                self.level = 2
                                self.groups = bubble_group
                                bg_group.empty()
                                bg_group.add(bg2,bg3)




""" 程序开始 """
if __name__ == '__main__':
    while True:
        pygame.init()                                                               # 初始化所有导入的pygame模块
        pygame.font.init()                                                          # 字体模块
        pygame.display.set_caption('flappy bird @fg')                               # 游戏窗口标题
        bgsize = bgwidth, bgheight = 400, 600                                       # 游戏窗口大小
        font = pygame.font.SysFont('', 50)                                          # 创建font对象
        screen = pygame.display.set_mode(bgsize, pygame.RESIZABLE)                  # 创建屏幕对象
        clock = pygame.time.Clock()                                                 # 时间模块
        title = 'Flappy   Bird!'                                                    # 游戏标题
        title_posxy = 50, 100                                                       # 游戏标题位置
        myfont = pygame.font.Font("./word/bitsybutton_v2.ttf", 90)                  # 创建字体对象
        score_font = pygame.font.Font("./word/bitsybutton_v2.ttf", 50)
        wt = myfont.render(title, True, (255, 255, 255))                            # 设置字体内容、抗锯齿、颜色
        title_selfxy = myfont.size(title)[0], myfont.size(title)[1]                 # 设置标题大小
        bg_land = pygame.image.load("./picture/land.png").convert()                 # land背景图片
        bg_sea = pygame.image.load("./picture/sea.png").convert()                   # sea背景图片
        start_image = pygame.image.load("./picture/start1.jpg").convert()           # 开始按钮
        music_image = pygame.image.load("./picture/music1.jpg").convert()           # 音乐按钮
        return_image = pygame.image.load("./picture/return1.jpg").convert()         # 返回按钮
        quit_image = pygame.image.load("./picture/quit1.jpg").convert()             # 退出按钮
        quit_music = pygame.image.load("./picture/music2.jpg").convert()            # 音乐停止按钮
        star = pygame.image.load("./picture/star.png").convert_alpha()              # 眩晕效果
        bird1 = pygame.image.load("./picture/bird1.png").convert_alpha()            # landbird小鸟
        bird2 = pygame.image.load("./picture/bird2.png").convert_alpha()
        bird3 = pygame.image.load("./picture/bird3.png").convert_alpha()
        bird4 = pygame.image.load("./picture/bird4.png").convert_alpha()            # seabird小鸟
        bird5 = pygame.image.load("./picture/bird5.png").convert_alpha()
        bird6 = pygame.image.load("./picture/bird6.png").convert_alpha()
        pineup = pygame.image.load("./picture/pipe2.png").convert_alpha()           # 上管道图片
        pinedown = pygame.image.load("./picture/pipe1.png").convert_alpha()         # 下管道图片
        bubble_image1 = pygame.image.load("./picture/smallbubble.png").convert_alpha()    # 泡泡图片
        bubble_image2 = pygame.image.load("./picture/midbubble.png").convert_alpha()
        bubble_image3 = pygame.image.load("./picture/bigbubble.png").convert_alpha()
        love_image = pygame.image.load("./picture/life2.png").convert_alpha()


        # love1 =Love(200)
        # love_group = pygame.sprite.Group()
        # love_group.add(love, love1)

        lives = 0
        bubble_num = 6
        cnt = 0                                                                     # 泡泡数量
        score = 0                                                                   # 游戏得分
        bg = Background(bg_land)
        bg1 = Background(bg_land,True)
        bg2 = Background(bg_sea)
        bg3 = Background(bg_sea,True)
        bg_group = pygame.sprite.Group()
        bg_group.add(bg,bg1)
        pipeline_group = pygame.sprite.Group()                                      # 管道组
        uppip = Pipeline(-300, pineup)                                              # 初始化管道类
        downpip = Pipeline(400, pinedown)
        pipeline_group.add(uppip, downpip)                                          # 添加管道类
        bubble_group = pygame.sprite.Group()                                        # 泡泡组
        love = Love()
        add_bubble(bubble_group, bubble_num)                                                # 添加泡泡类
        sr_button = Button()                                                        # 初始化按钮类
        mq_button = Button()
        quitmusic = Button()
        music = Music()                                                             # 初始化音乐类
        bird = Bird()                                                               # 初始化鸟类
        game = Game()                                                               # 初始化游戏类
        game.begin()                                                                # 游戏开始
